from common import Instruction, Status_AIV, Global
from .distributor import distribute_task
from .single import arrange_aiv


class Dispatcher(object):
    '''
    调度类，实例化后可调用以下接口
    - arrange_for_aiv 为一辆车路径规划
    - traffic_arrangement 对场景中所有的车作交通控制
    - arrange_for_tasks 为输入的一组任务安排最合适的车辆
    '''

    def __init__(self, path_planner, config={}):
        '''
            参数如下
            - path_planner 路径规划模块
            - config 调度模块基本配置，结构如下
            {
                'window_size': 窗口大小,
                'zones': 特殊区域
            }
        '''
        self.path_planner = path_planner

        self.window_size = config.get('window_size', 4)
        self.zones = config.get('zones', {})

    def classify_aiv(self, aivs: list) -> dict:
        '''
            将无人车按状态进行分类
            + id
            + battary
            + status
            + capacity
            + taskid
            + position
        '''
        classified = {}

        for status in Status_AIV:
            classified[status.name] = []
        
        for aiv in aivs:
            classified[aiv['status']].append(aiv)

        return classified

    def arrange_for_tasks(self, tasks: list, aivs: list, nowtime: int) -> list:
        '''
            【多车调度】传入一组任务和车辆信息时，为这组任务分配合适的车辆 \n
            输入如下：
            + 一组任务
            + 一组小车
            + 当前时间

            返回的是一组指令，一条指令包括如下信息：
            + 什么车：车辆ID
            + 做什么任务：任务ID
            + 去哪里：终点
            + 何时动身：发车时间
        '''
        # 从所有车辆中筛选可用车辆（空闲状态、等待状态、刚卸完货的车）
        classified = self.classify_aiv(aivs)
        available_aivs = classified[Status_AIV.Free.name]

        # 从推过来的任务中筛选（一定数量的）（还没开始做的）任务去分配
        waiting_tasks = tasks[:self.window_size]

        # 调用二分图匹配相关算法获取任务分配的结果
        instructions = distribute_task(waiting_tasks, available_aivs, nowtime, self.path_planner)
        
        # 路径规划
        self.path_planner.ins_to_points(instructions)

        return instructions

    def arrange_for_aiv(self, aiv, signal) -> Instruction:
        '''
            【单车调度】传入一辆 aiv 的信息，安排这辆车去某个地方（充电区，加油站，食堂等情况）\n
            通常是由车辆主动申请调度，是在车辆完成自己的任务后，或者司机通过面板主动请求。输入：
            + aiv 请求被调度的车辆
            + signal 车辆行为信号（充电，加油，任务完成等）

            返回的是一个指令，指令内容包括：
            + 什么车：车辆ID
            + 去哪里：终点
        '''
        # 请求时并行处理的，在这里访问公共资源（目的地的空位）可以加锁

        # 根据信号，选取相应的候选目的地。
        # 充电信号就去充电区，加油信号就去加油站，任务完成后的休息信号就去停车场
        destinations = self.zones.get(signal, None)

        if not destinations:
            # 错误信号，可记录在日志中
            print('[err]: aiv:%s signal:%s dispatcher.py line 97' % (aiv.id, signal))
            return None
        
        # 找最短距离
        ins = arrange_aiv(aiv, signal, destinations, self.path_planner)

        # 路径规划
        self.path_planner.departure_to_points(ins)

        return ins

    def traffic_arrangement(self, aivs) -> list:
        '''
            安排场地上所有的路口，对有冲突风险的车辆进行加减速协调交通；\n
            对经过了速度调整后的车辆，在确认安全后调回正常速度 \n
            输入场地上所有的 AIV 当前的运动信息，包括如下信息
            + 车辆当前的位置
            + 车辆的速度
            + 车辆的运动方向

            输出是一组指令，指令内容包括
            + 什么车：车辆ID
            + 做什么动作：加速或者减速
            + 这个动作做多久：时间
        '''
        pass

